package com.heihei.authority.core.authentication.mobile;

import com.heihei.authority.core.properties.FeiSecurityConstants;
import com.heihei.authority.core.properties.MySecurityProperties;
import com.heihei.authority.core.validate.code.ValidateCodeException;
import com.heihei.authority.core.validate.code.ValidateCodeProcessor;
import com.heihei.authority.core.validate.code.sms.SmsCode;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 *  短信验证码  校验filter
 *  用于校验短信验证码正确与否
 *
 * @version 1.0
 * @since JDK1.8
 * @author duanhengfei
 * @date 2021年04月12日 15:53:37
 */
public class SmsCodeFilter extends OncePerRequestFilter implements InitializingBean {

	/** 验证失败处理器 */
	private AuthenticationFailureHandler authenticationFailureHandler;

	/** 用于处理session */
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

	/** 存储用户配置的urls */
	private Set<String> urls = new HashSet<>();

	/** 用于存储用户配置 */
	private MySecurityProperties mySecurityProperties;

	/** url匹配工具类 */
	private AntPathMatcher pathMatcher = new AntPathMatcher();

	/**
	 * 初始化url属性
	 *
	 * @param
	 * @Return
	 * @throws
	 * @author duanhengfei
	 * @date 2021年04月11日 10:07:08
	 */
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
		String configFilterUrlStr = mySecurityProperties.getCode().getSms().getFilterUrl();
		if(StringUtils.isNotBlank(configFilterUrlStr)) {
			String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(configFilterUrlStr, ",");
			for (String configUrl : configUrls) {
				urls.add(configUrl);
			}
		}
		//把登录的url直接加进来，登录是肯定要验证码的
		urls.add(FeiSecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE);
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
		boolean action = false;
		for (String url : urls) {
			if(pathMatcher.match(url, request.getRequestURI())) {
				action = true;
				break;
			}
		}
		if(action) {
			try {
				validate(new ServletWebRequest(request));
			} catch (ValidateCodeException e) {
				authenticationFailureHandler.onAuthenticationFailure(request, response, e);
				return;
			}
		}
		filterChain.doFilter(request, response);
	}

	/**
	 * 校验验证码
	 *
	 * @param request
	 * @Return
	 * @throws
	 * @author duanhengfei
	 * @date 2021年04月04日 22:33:48
	 */
	private void validate(ServletWebRequest request) throws ServletRequestBindingException {
		//从session中取出来的值
		SmsCode codeInSession = (SmsCode) sessionStrategy.getAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX + "SMS");
		if(codeInSession == null) {
			throw new ValidateCodeException("无效短信验证码");
		}
		//从请求中取出来的值
		String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), FeiSecurityConstants.DEFAULT_PARAMETER_NAME_CODE_SMS);
		if(StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException("验证码值不能为空");
		}
		if(codeInSession.isExpired()) {
			sessionStrategy.removeAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX + "SMS");
			throw new ValidateCodeException("验证码已过期");
		}
		if(!StringUtils.equalsIgnoreCase(codeInSession.getCode(), codeInRequest)) {
			throw new ValidateCodeException("验证码不匹配");
		}
		sessionStrategy.removeAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX + "SMS");
	}


	public AuthenticationFailureHandler getAuthenticationFailureHandler() {
		return authenticationFailureHandler;
	}

	public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
		this.authenticationFailureHandler = authenticationFailureHandler;
	}

	public MySecurityProperties getMySecurityProperties() {
		return mySecurityProperties;
	}

	public void setMySecurityProperties(MySecurityProperties mySecurityProperties) {
		this.mySecurityProperties = mySecurityProperties;
	}
}
